package com.small.base.qiniu.core;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.processing.OperationManager;
import com.qiniu.processing.OperationStatus;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.qiniu.util.StringUtils;
import com.qiniu.util.UrlSafeBase64;
import com.small.base.qiniu.QiniuProperties;
import com.small.base.qiniu.entity.QiniuEntity;
import com.small.base.qiniu.exce.SmallQiniuException;
import com.small.base.qiniu.exce.SmallQiniuExceptionEnum;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.UUID;

/**
 * QiniuService
 *
 * @author miajio
 */
public class QiniuService {

    /**
     * qi niu properties
     */
    private QiniuProperties qiniuProperties;
    /**
     * qi niu auth
     */
    private Auth auth;
    /**
     * qi niu configuration
     */
    private Configuration configuration;

    /**
     * init qi niu service
     *
     * @param qiniuProperties
     */
    public QiniuService(QiniuProperties qiniuProperties) {
        this.qiniuProperties = qiniuProperties;
        this.auth = Auth.create(this.qiniuProperties.getAccessKey(), this.qiniuProperties.getSecretKey());
        this.configuration = new Configuration(getRegion());
    }

    /**
     * get qi niu auth
     *
     * @return
     */
    private Auth getAuth() {
        if (null != this.auth) {
            return this.auth;
        }
        this.auth = Auth.create(this.qiniuProperties.getAccessKey(), this.qiniuProperties.getSecretKey());
        return this.auth;
    }

    /**
     * 获取服务器地区 (根据配置文件动态获取)
     *
     * @return
     */
    private Region getRegion() {
        switch (this.qiniuProperties.getRegion()) {
            case "huadong":
                return Region.huadong();
            case "huabei":
                return Region.huabei();
            case "huanan":
                return Region.huanan();
            case "beimei":
                return Region.beimei();
            case "xinjiapo":
                return Region.xinjiapo();
            default:
                return Region.autoRegion();
        }
    }

    /**
     * 获取上传凭证
     *
     * @param bucketName 空间名
     * @return
     */
    public String getUploadToken(String bucketName) {
        return getAuth().uploadToken(bucketName);
    }

    /**
     * 获取覆盖上传凭证 - 文件覆盖方案
     *
     * @param bucketName 空间名
     * @param fileKey    文件key
     * @return
     */
    public String getUploadToken(String bucketName, String fileKey) {
        return getAuth().uploadToken(bucketName, fileKey);
    }

    /**
     * 自定义上传回复凭证 - 自定义上传成功后返回对应json设置方案
     *
     * @param bucketName 空间名
     * @param returnBody 自定义返回对象
     *                   demo:
     *                   {\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\",\"fsize\":$(fsize)}
     * @param timeOut    超时时间
     * @return
     */
    public String customUploadToken(String bucketName, String returnBody, Long timeOut) {
        StringMap putPolicy = new StringMap();
        putPolicy.put("returnBody", returnBody);
        return getAuth().uploadToken(bucketName, null, timeOut, putPolicy);
    }

    /**
     * 自定义上传回调凭证 - 上传成功后回调服务器方案
     *
     * @param bucketName       空间名
     * @param callbackUrl      回调地址
     * @param callbackBody     自定义返回对象-> 回调调用服务器参数
     *                         demo:
     *                         {\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\",\"fsize\":$(fsize)}
     * @param callbackBodyType 回调的数据类型 -> application/json
     * @param timeOut          超时时间
     * @return
     */
    public String callbackUploadToken(String bucketName, String callbackUrl, String callbackBody, String callbackBodyType, Long timeOut) {
        StringMap putPolicy = new StringMap();
        putPolicy.put("callbackUrl", callbackUrl);
        putPolicy.put("callbackBody", callbackBody);
        putPolicy.put("callbackBodyType", callbackBodyType);
        return getAuth().uploadToken(bucketName, null, timeOut, putPolicy);
    }

    /**
     * file upload
     * 文件上传
     *
     * @param bucketName 空间名
     * @param filePath   文件地址 -> 服务器本地文件路径
     * @param setKey     设置的文件名 -> 默认不指定key的情况下, 以文件内容的hash值作为文件名
     * @return
     * @throws QiniuException
     */
    public QiniuEntity fileUpload(String bucketName, String filePath, String setKey) throws QiniuException {
        UploadManager uploadManager = new UploadManager(this.configuration);
        String upToken = getUploadToken(bucketName);
        Response response = uploadManager.put(filePath, setKey, upToken);
        return response.jsonToObject(QiniuEntity.class);
    }

    /**
     * byte array upload
     * 字节数组上传
     *
     * @param bucketName  空间名
     * @param uploadBytes 上传的byte数组
     * @param setKey      设置的文件名 -> 默认不指定key的情况下, 以文件内容的hash值作为文件名
     * @return
     * @throws QiniuException
     */
    public QiniuEntity bytesUpload(String bucketName, byte[] uploadBytes, String setKey) throws QiniuException {
        UploadManager uploadManager = new UploadManager(this.configuration);
        String upToken = getUploadToken(bucketName);
        Response response = uploadManager.put(uploadBytes, setKey, upToken);
        return response.jsonToObject(QiniuEntity.class);
    }

    /**
     * input stream upload
     * 数据流上传
     *
     * @param bucketName  空间名
     * @param inputStream 数据流
     * @param setKey      设置的文件名 -> 默认不指定key的情况下, 以文件内容的hash值作为文件名
     * @return
     * @throws QiniuException
     */
    public QiniuEntity inputStreamUpload(String bucketName, InputStream inputStream, String setKey) throws QiniuException {
        UploadManager uploadManager = new UploadManager(this.configuration);
        String upToken = getUploadToken(bucketName);
        Response response = uploadManager.put(inputStream, setKey, upToken, null, null);
        return response.jsonToObject(QiniuEntity.class);
    }

    /**
     * 获取公共空间中的文件 -> 需配置 domain 域名
     *
     * @param fileName 文件名
     * @param domain   域名 -> 七牛云域名
     * @return
     */
    public String getPublicFileUrl(String fileName, String domain) {
        if (null == domain || domain.isEmpty()) {
            throw new SmallQiniuException(SmallQiniuExceptionEnum.DOMAIN_IS_EMPTY);
        }
        return String.format("%s/%s", domain, fileName);
    }

    /**
     * 获取私有空间中的文件 -> 需配置 domain 域名
     *
     * @param fileName 文件名
     * @param domain   域名 -> 七牛云域名
     * @param timeOut  超时时间 (秒)
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getPrivateFileUrl(String fileName, String domain, Long timeOut) throws UnsupportedEncodingException {
        String encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20");
        String publicUrl = getPublicFileUrl(encodedFileName, domain);
        return getAuth().privateDownloadUrl(publicUrl, timeOut);
    }

    /**
     * 获取私有空间中的文件 -> 需配置 domain 域名
     *
     * @param fileName 文件名
     * @param domain   域名 -> 七牛云域名
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getPrivateFileUrl(String fileName, String domain) throws UnsupportedEncodingException {
        String encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20");
        String publicUrl = getPublicFileUrl(encodedFileName, domain);
        return getAuth().privateDownloadUrl(publicUrl);
    }

    /**
     * get file info
     * 获取文件信息
     *
     * @param bucketName 空间名
     * @param fileKey    文件key
     * @return
     * @throws QiniuException
     */
    public FileInfo getFileInfo(String bucketName, String fileKey) throws QiniuException {
        BucketManager bucketManager = new BucketManager(getAuth(), this.configuration);
        return bucketManager.stat(bucketName, fileKey);
    }

    /**
     * update file type
     * 修改文件类型
     *
     * @param bucketName  空间名
     * @param fileKey     文件key
     * @param newMimeType 新文件类型
     * @throws QiniuException
     */
    public void updateFileType(String bucketName, String fileKey, String newMimeType) throws QiniuException {
        BucketManager bucketManager = new BucketManager(getAuth(), this.configuration);
        bucketManager.changeMime(bucketName, fileKey, newMimeType);
    }

    /**
     * file compression
     * 文件压缩
     *
     * @param bucketName   空间名
     * @param domain       域名 -> 七牛云域名
     * @param fileKey      压缩后名称
     * @param fileUrlArray 需压缩的文件访问地址
     */
    public OperationStatus fileCompression(String bucketName, String domain, String fileKey, String[] fileUrlArray) throws QiniuException, UnsupportedEncodingException {
        String content = "";
        for (String fileUrl : fileUrlArray) {
            String safeUrl = "/url/" + UrlSafeBase64.encodeToString(getPrivateFileUrl(fileUrl, domain));
            content += ((StringUtils.isNullOrEmpty(content) ? "" : "\n") + safeUrl);
        }
        String txtKey = UUID.randomUUID().toString().replaceAll("-", "") + "-index.txt";
        String uploadToken = this.auth.uploadToken(bucketName, txtKey, 3600, new StringMap().put("insertOnly", 0));
        UploadManager uploadManager = new UploadManager(this.configuration);
        uploadManager.put(content.getBytes(), txtKey, uploadToken);
        String fops = "mkzip/4/encoding/" + UrlSafeBase64.encodeToString("gbk") + "|saveas/" + UrlSafeBase64.encodeToString(bucketName + ":" + fileKey + ".zip");
        OperationManager operationManager = new OperationManager(getAuth(), this.configuration);
        StringMap params = new StringMap();
        String persistentId = operationManager.pfop(bucketName, txtKey, fops, params);
        return operationManager.prefop(persistentId);
    }

}
